﻿package kr.flas.net
{
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	/**
	 * PostRequest는 웹서버에 여러개의 파일을 전송할 수 있게 하기위한 클래스입니다.
	 * <a href="http://www.rfc-editor.org/rfc/rfc1867.txt" target="_blank">RFC1867 표준</a>에 따라 <code>URLRequest</code> 객체를 구성해줍니다.
	 * @langversion ActionScript 3.0
	 * @playerversion AIR 1.0 Flash Player 9
	 * @author FLAS
	 * 
	 */	
	public class PostRequest
	{
		protected var _main_boundary;
		protected var _array_form_data:Object;
		protected var _array_file_data:Object;
		
		/**
		 * 새 PostRequest 인스턴스를 만듭니다.
		 * PostRequest 인스턴스를 만든다음 PostRequest.addFormData 또는 PostRequest.addFileData 메서드로 항목을 추가하고 PostRequest.getURLRequest 메서드로 <code>URLRequest</code> 객체를 받아 사용합니다.
		 * @langversion ActionScript 3.0
	 	 * @playerversion AIR 1.0 Flash Player 9
		 */
		public function PostRequest()
		{
			_main_boundary = getBoundary();
			_array_form_data = new Object();
			_array_file_data = new Object();
		}
		
		/**
		 * 인수를 추가합니다.
		 * @langversion ActionScript 3.0
	 	 * @playerversion AIR 1.0 Flash Player 9
		 * @param form_name 추가할 인수명입니다.
		 * @param data 인수의 데이터입니다.
		 * 
		 */		
		public function addFormData(form_name:String, data:String):void
		{
			if(!data){data="";}
			_array_form_data[form_name] = data;
		}
		
		/**
		 * 파일을 추가합니다.
		 * @langversion ActionScript 3.0
	 	 * @playerversion AIR 1.0 Flash Player 9
		 * @param form_name 추가할 인수명입니다.
		 * @param file_name 파일명입니다.
		 * @param data 파일 데이터입니다.
		 * @param content_type 파일 데이터의 타입입니다. 기본값은 PNG 이미지를 나타내는 "image/png"입니다.
		 * 
		 */		
		public function addFileData(form_name:String, file_name:String, data:ByteArray, content_type:String="image/png"):void
		{
			if(!_array_file_data[form_name])
			{_array_file_data[form_name] = new Object();}
			_array_file_data[form_name][file_name] = new Object();
			_array_file_data[form_name][file_name].type = content_type;
			_array_file_data[form_name][file_name].data = data;
		}
        
        /**
         * <code>URLLoader</code> 객체에 바로 사용해서 파일을 전송하도록 <code>URLRequest</code> 객체를 구성해 반환합니다.
         * @langversion ActionScript 3.0
	 	 * @playerversion AIR 1.0 Flash Player 9
         * @param url 파일을 전송할 경로입니다.
         * @return 파일을 전송할 수 있도록 구성된 <code>URLRequest</code> 객체입니다.
         * 
         */        
        public function getURLRequest(url:String):URLRequest
        {
        	var _request = new URLRequest();
        	_request.url = url;
        	_request.method = URLRequestMethod.POST;
        	_request.contentType = getContentType();
        	_request.data = getPostData();
        	
        	return _request;
        }
        
        /**
         *  <code>URLRequest</code> 객체의 <code>contentType</code> 속성에 등록할 문자열을 반환합니다.
         * @langversion ActionScript 3.0
	 	 * @playerversion AIR 1.0 Flash Player 9
         * @return <code>contentType</code> 속성에 등록할 문자열입니다.
         * 
         */        
        public function getContentType():String
        {
        	return 'multipart/form-data, boundary='+_main_boundary;
        }
		
		/**
		 * 추가된 인수와 파일을 <code>ByteArray</code> 객체에 담아 <code>URLRequest</code> 객체의 <code>data</code> 속성에 추가해 사용하도록 데이터를 반환합니다.
		 * @langversion ActionScript 3.0
	 	 * @playerversion AIR 1.0 Flash Player 9  
		 * @return 인수와 파일을 구성한 <code>ByteArray</code> 객체입니다.
		 * 
		 */		
		public function getPostData():ByteArray
		{
			var _form_name:String;
			var _file_name:String;
			var _boundary:String;
			var _content_type:String;
			var _data:ByteArray;
			
			var _post_data:ByteArray;
			_post_data = new ByteArray();
			_post_data.endian = Endian.BIG_ENDIAN;
			
			/* 속성 추가 */
			for(_form_name in _array_form_data)
			{
				_post_data = writeBoundary(_post_data, _main_boundary);
				_post_data = writeLineBreak(_post_data);
				_post_data = writeString(_post_data, 'Content-Disposition: form-data; name="' + _form_name + '"');
				_post_data = writeLineBreak(_post_data);
				_post_data = writeLineBreak(_post_data);
				_post_data.writeUTFBytes(_array_form_data[_form_name]);
				_post_data = writeLineBreak(_post_data);
			}
			
			/* 파일 추가 */
			for(_form_name in _array_file_data)
			{
				for(_file_name in _array_file_data[_form_name])
				{
					_content_type = _array_file_data[_form_name][_file_name].type;
					_data = _array_file_data[_form_name][_file_name].data;
					
					_post_data = writeBoundary(_post_data, _main_boundary);
					_post_data = writeLineBreak(_post_data);
					_post_data = writeString(_post_data, 'Content-Disposition: form-data; name="' + _form_name + '"; filename="' + _file_name + '"');
					_post_data = writeLineBreak(_post_data);
					_post_data = writeString(_post_data, 'Content-Type: application/octet-stream');
					_post_data = writeLineBreak(_post_data);
					_post_data = writeLineBreak(_post_data);
					_post_data.writeBytes(_data, 0, _data.length);
					_post_data = writeLineBreak(_post_data);
				}
			}
			
			/* 닫기 */
			_post_data = writeBoundary(_post_data, _main_boundary);
			_post_data = writeDoubleDash(_post_data);
			_post_data = writeLineBreak(_post_data);
			
			return _post_data;
		}
		
		/* 바운더리 값 생성 */
		protected function getBoundary():String
		{
			var _boundary = "";
			if(_boundary.length == 0)
			{
				for (var i:int = 0; i < 0x20; i++ )
				{
					_boundary += String.fromCharCode( int( 97 + Math.random() * 25 ) );
				}
			}
			
			return _boundary;
        }
		
		/* 바이너리에 문자열 쓰기 */
		protected function writeString(_post_data:ByteArray, str:String):ByteArray
		{
			for (var i:int = 0; i < str.length; i++ )
			{
				_post_data.writeByte( str.charCodeAt( i ) );
			}
			
			return _post_data
		}
		
		/* 바이너리에 바운더리 쓰기 */
		protected function writeBoundary(_post_data:ByteArray, _boundary:String):ByteArray
		{
			_post_data = writeDoubleDash(_post_data);
			writeString(_post_data, _boundary);
			
			return _post_data;
        }
		
		/* 바이너리에 라인 쓰기 */
        protected function writeLineBreak(_post_data:ByteArray):ByteArray
        {
            _post_data.writeShort(0x0d0a);
            return _post_data;
        }
		
		/* 바이너리에 "--" 쓰기 */
        protected function writeDoubleDash(_post_data:ByteArray):ByteArray
        {
            _post_data.writeShort(0x2d2d);
            return _post_data;
        }
	}
}